#include "cmodelmatrix2d.h"

/**
 * CModelMatrix2D - creates 3x3 identity matrix
 */
template <typename T>
CModelMatrix2D<T>::CModelMatrix2D() {
  this->init(this->M3X3_SIZE, this->M3X3_SIZE);
}
//---------------------------------------------------------------------------

/**
 * CModelMatrix2D - creates 3x3 matrix with given elements
 * @param items
 */
template <typename T>
CModelMatrix2D<T>::CModelMatrix2D(T items[]) {
  this->init(this->M3X3_SIZE, this->M3X3_SIZE, items);
}
//---------------------------------------------------------------------------

/**
 * CModelMatrix2D - copy constructor (does deep copy)
 * @param m
 */
template <typename T>
CModelMatrix2D<T>::CModelMatrix2D(const CModelMatrix2D<T> &m) {
  this->copyFrom(m);
}
//---------------------------------------------------------------------------

/**
 * CModelMatrix2D - copy constructor
 * @param m
 */
template <typename T>
CModelMatrix2D<T>::CModelMatrix2D(const CMatrixBase<T> &m) {

  // If matrix is not 3x3, exception
  if (m.getNumOfRows() != this->M3X3_SIZE ||
      m.getNumOfCols() != this->M3X3_SIZE) {
    throw CInvalidSizeException(getClassName(), "CModelMatrix2D");
  }
  this->copyFrom(m);
}
//---------------------------------------------------------------------------

/**
 * ~CModelMatrix2D - destructor
 */
template <typename T>
CModelMatrix2D<T>::~CModelMatrix2D() {
  this->free();
}
//---------------------------------------------------------------------------

/**
 * operator= - matrix assignment (does deep copy)
 * @param m
 * @return this
 */
template <typename T>
CModelMatrix2D<T>& CModelMatrix2D<T>::operator = (const CModelMatrix2D<T> &m) {
  this->free();
  this->copyFrom(m);
  return *this;
}
//---------------------------------------------------------------------------

/**
 * operator+ - adds two given 3x3 matrices
 * @param m
 * @return sum
 */
template <typename T>
CModelMatrix2D<T> CModelMatrix2D<T>::operator + (
  const CModelMatrix2D<T> &m) const {
  return CModelMatrix2D<T>(*this) += m;
}
//---------------------------------------------------------------------------

/**
 * operator- - subtracts two given 3x3 matrices
 * @param m
 * @return difference
 */
template <typename T>
CModelMatrix2D<T> CModelMatrix2D<T>::operator - (
  const CModelMatrix2D<T> &m) const {
  return CModelMatrix2D<T>(*this) -= m;
}
//---------------------------------------------------------------------------

/**
 * operator* - multiplies two given 3x3 matrices
 * @param m
 * @return product
 */
template <typename T>
CModelMatrix2D<T> CModelMatrix2D<T>::operator * (
  const CModelMatrix2D<T> &m) const {
  return CModelMatrix2D<T>(*this) *= m;
}
//---------------------------------------------------------------------------

/**
 * operator* - multiplies given 3x3 matrix by given number
 * @param num
 * @return result
 */
template <typename T>
CModelMatrix2D<T> CModelMatrix2D<T>::operator * (T num) const {
  return CModelMatrix2D<T>(*this) *= num;
}
//---------------------------------------------------------------------------

/**
 * operator/ - divides given 3x3 matrix by given number
 * @param num
 * @return result
 */
template <typename T>
CModelMatrix2D<T> CModelMatrix2D<T>::operator / (T num) const {
  return CModelMatrix2D<T>(*this) /= num;
}
//---------------------------------------------------------------------------

/**
 * getNumOfRows - return number of 3x3 matrix rows
 * @return always three
 */
template <typename T>
TIndex CModelMatrix2D<T>::getNumOfRows() const {
  return this->M3X3_SIZE;
}
//---------------------------------------------------------------------------

/**
 * getNumOfCols - return number of 3x3 matrix columns
 * @return always three
 */
template <typename T>
TIndex CModelMatrix2D<T>::getNumOfCols() const {
  return this->M3X3_SIZE;
}
//---------------------------------------------------------------------------

/**
 * translate - translates 3x3 matrix
 * @param x translation in x-axis
 * @param y translation in y-axis
 */
template <typename T>
void CModelMatrix2D<T>::translate(T x, T y) {

  // Create temporary translation matrix
  CModelMatrix2D<T> tmp;
  tmp.clearToIdentity();
  tmp.set(2, 0, x);
  tmp.set(2, 1, y);

  // Multiply temporary translation matrix by this
  *this = tmp * (*this);
}
//---------------------------------------------------------------------------

/**
 * translate - translates 3x3 matrix
 * @param v translation 2D vector
 */
template <typename T>
void CModelMatrix2D<T>::translate(const CVector2D<T> &v) {
  translate(v.getX(), v.getY());
}
//---------------------------------------------------------------------------

/**
 * rescale - scales 3x3 matrix
 * @param x scale factor for x-axis
 * @param y scale factor for y-axis
 */
template <typename T>
void CModelMatrix2D<T>::rescale(T x, T y) {

  // Create temporary scale matrix
  CModelMatrix2D<T> tmp;
  tmp.clearToIdentity();
  tmp.set(0, 0, x);
  tmp.set(1, 1, y);

  // Multiply scale translation matrix by this
  *this = tmp * (*this);
}
//---------------------------------------------------------------------------

/**
 * rescale - scales 3x3 matrix
 * @param v 2D vector of scale factors
 */
template <typename T>
void CModelMatrix2D<T>::rescale(const CVector2D<T> &v) {
  rescale(v.getX(), v.getY());
}
//---------------------------------------------------------------------------

/**
 * rotate - rotates 3x3 matrix
 * @param angle angle (positive value for counter-clockwise rotation)
 */
template <typename T>
void CModelMatrix2D<T>::rotate(T angle) {

  // Temporary sine and cosine
  T s((T) sinf(CMath<T>::degToRad(angle)));
  T c((T) cosf(CMath<T>::degToRad(angle)));

  // Create temporary rotation matrix
  CModelMatrix2D<T> tmp;
  tmp.clearToIdentity();
  tmp.set(0, 0, c);
  tmp.set(0, 1, -s);
  tmp.set(1, 0, c);
  tmp.set(1, 1, s);

  // Multiply rotation translation matrix by this
  *this = tmp * (*this);
}
//---------------------------------------------------------------------------

/**
 * mul - multiplies 3x3 matrix by vector
 * @param m matrix
 * @param v vector
 * @return result (vector)
 */
template <typename T>
CVector2D<T> CModelMatrix2D<T>::mul(const CModelMatrix2D<T> &m,
                                    const CVector2D<T> &v) {

  // Temporary vector (resized to size 3)
  CVector<T> tmp(CModelMatrix2D<T>::M3X3_SIZE);
  tmp.set(0, v.getX());
  tmp.set(1, v.getY());
  tmp.set(2, 0);

  // Multiply matrix by vector
  CVector<T> result(CMatrixBase<T>::mul(m, tmp));
  return CVector2D<T>(result.get(0), result.get(1));
}
//---------------------------------------------------------------------------

/**
 * mul - multiplies vector by vector 3x3 matrix
 * @param v vector
 * @param m matrix
 * @return result (vector)
 */
template <typename T>
CVector2D<T> CModelMatrix2D<T>::mul(const CVector2D<T> &v,
                                    const CModelMatrix2D<T> &m) {

  // Temporary vector (resized to size 3)
  CVector<T> tmp(CModelMatrix2D<T>::M3X3_SIZE);
  tmp.set(0, v.getX());
  tmp.set(1, v.getY());
  tmp.set(2, 0);

  // Multiply vector by matrix
  CVector<T> result(CMatrixBase<T>::mul(tmp, m));
  return CVector2D<T>(result.get(0), result.get(1));
}
//---------------------------------------------------------------------------

template class CModelMatrix2D<float>;
template class CModelMatrix2D<double>;
